within BusinessSimulation.MoleculesOfStructure.Incubators;

model Account "Basic model of an account with optional interest"
  import BusinessSimulation.Types.InitializationOptions;
  import BusinessSimulation.Units.*;
  import BusinessSimulation.Constants.*;
  extends Icons.SubsystemIncubator;
  extends Interfaces.Basics.OutputTypeChoice;
  Interfaces.Connectors.StockPort inflow "Increasing the balance of the account" annotation(Placement(visible = true, transformation(origin = {-150, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Interfaces.Connectors.StockPort outflow "Decreasing the balance of the account" annotation(Placement(visible = true, transformation(origin = {150, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 0}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Interfaces.Connectors.RealInput u_plus if hasRateInputs "Rate of inflow increasing the account (or net rate of flow)" annotation(Placement(visible = true, transformation(origin = {-145, 60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-50, 100}, extent = {{-10, -10}, {10, 10}}, rotation = -90)));
  Interfaces.Connectors.RealInput u_minus if not hasNetRateInput and hasRateInputs "Rate of outflow decreasing the account" annotation(Placement(visible = true, transformation(origin = {-145, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {50, 100}, extent = {{-10, -10}, {10, 10}}, rotation = -810)));
  Interfaces.Connectors.RealInput u_interestPlus if withInterest and not hasConstantInterest "Fractional interest rate for positive balances (optional)" annotation(Placement(visible = true, transformation(origin = {-145, -40}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-100, 50}, extent = {{-10, -10}, {10, 10}}, rotation = -1080)));
  Interfaces.Connectors.RealInput u_interestMinus if withInterest and not hasConstantInterest "Fractional interest rate for negative balances (optional)" annotation(Placement(visible = true, transformation(origin = {-145, -60}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {100, 50}, extent = {{-10, -10}, {10, 10}}, rotation = -900)));
  RealOutput y_intEarned if withInterest "Interest earned (optional)" annotation(Placement(visible = true, transformation(origin = {161.973, -50}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {-105, -50}, extent = {{10, -10}, {-10, 10}}, rotation = 0)));
  RealOutput y_intPaid if withInterest "Interest paid (optional)" annotation(Placement(visible = true, transformation(origin = {161.742, -35}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {105, -50}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  RealOutput y "Balance of the account (e.g., amount in the stock)" annotation(Placement(visible = true, transformation(origin = {162.01, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0), iconTransformation(origin = {0, 104}, extent = {{-10, -10}, {10, 10}}, rotation = -270)));
  parameter OutputType initialBalance = 0 "Initial balance of the account";
  parameter Rate interestRatePlus = unspecified "Constant fractional interest rate for positive balances (optional)" annotation(Dialog(enable = withInterest and hasConstantInterest));
  parameter Rate interestRateMinus = unspecified "Constant fractional interest rate for negative balances (optional)" annotation(Dialog(enable = withInterest and hasConstantInterest));
  parameter Boolean hasRateInputs = true "= true, if input connectors should allow giving transaction rates increasing/decreasing the balance of the account" annotation(Evaluate = true, Dialog(group = "Structural Parameters"));
  parameter Boolean hasNetRateInput = false "= true, if the rate of flow is given a net rate (u_plus only)" annotation(Evaluate = true, Dialog(group = "Structural Parameters"));
  parameter Boolean withInterest = false "= true, if positive and negative balances lead to interest earned or paid" annotation(Evaluate = true, Dialog(group = "Structural Parameters"));
  parameter Boolean hasConstantInterest = true "= true, if the optional interest rates are given as constant parameters" annotation(Evaluate = true, Dialog(group = "Structural Parameters"));
  parameter InitializationOptions init = modelSettings.init "Provide InitializationOptions (Free, FixedValue, SteadyState) (accountBalance.init)" annotation(Evaluate = true, Dialog(tab = "Advanced"));
  parameter Boolean strict = true "= true, if strict limits with noEvent(..) (positiveBalance.strict)" annotation(Evaluate = true, Dialog(tab = "Advanced"));
  outer ModelSettings modelSettings;
protected
  Stocks.InformationLevel accountBalance(hasStockInfoOutput = false, useAssert = false, causeError = false, initialValue = initialBalance, init = init) "Balance of the account" annotation(Placement(visible = true, transformation(origin = {-0, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  SourcesOrSinks.ExogenousChange netFlow if hasRateInputs "Net flow to or from the account" annotation(Placement(visible = true, transformation(origin = {-50, 20}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.Gain negativeRate(c = -1) if not hasNetRateInput and hasRateInputs "The rate of decrease is turned into a negative rate" annotation(Placement(visible = true, transformation(origin = {-110, 40}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.Add_2 netRate(redeclare replaceable type OutputType = Unspecified) if not hasNetRateInput and hasRateInputs "Net rate of flow" annotation(Placement(visible = true, transformation(origin = {-80, 45}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.PassThrough netRateInput if hasNetRateInput and hasRateInputs "Input u_plus net rate of flow" annotation(Placement(visible = true, transformation(origin = {-80, 80}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.ConstantConverter interestPlus(value = interestRatePlus) if withInterest and hasConstantInterest "Constant rate of interest for positive balances" annotation(Placement(visible = true, transformation(origin = {-120, -50}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.ConstantConverter interestMinus(value = interestRateMinus) if withInterest and hasConstantInterest "Constant rate of interest for negative balances" annotation(Placement(visible = true, transformation(origin = {-120, -80}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.ZeroIfNegative positiveBalance(strict = strict) if withInterest "Positive balance or zero" annotation(Placement(visible = true, transformation(origin = {-50, -10}, extent = {{10, -10}, {-10, 10}}, rotation = 0)));
  Converters.ZeroIfNegative negativeBalance(strict = strict) if withInterest "Negative balance or zero" annotation(Placement(visible = true, transformation(origin = {60, 15}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.Gain invertedBalance(c = -1) if withInterest annotation(Placement(visible = true, transformation(origin = {30, 15}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.Product_2 rateDecline if withInterest "Rate of decline" annotation(Placement(visible = true, transformation(origin = {70, -10}, extent = {{10, -10}, {-10, 10}}, rotation = 0)));
  SourcesOrSinks.Decline interestPaid if withInterest "Interest decreasing the balance" annotation(Placement(visible = true, transformation(origin = {40, -30}, extent = {{10, -10}, {-10, 10}}, rotation = 0)));
  SourcesOrSinks.Growth interestEarned if withInterest "Interest payments increasing the balance" annotation(Placement(visible = true, transformation(origin = {-36.649, -45}, extent = {{-10, -10}, {10, 10}}, rotation = 0)));
  Converters.Product_2 rateIncrease if withInterest "Rate of decline" annotation(Placement(visible = true, transformation(origin = {-80, -25}, extent = {{-10, -10}, {10, 10}}, rotation = 360)));
  parameter Boolean interestRates_enabled = withInterest and hasConstantInterest "Switch for interestRatePlus parameter display" annotation(Evaluate = true, Dialog(enable = false, tab = "Initialization"));
equation
  // check that parameter inputs are explicitly given if needed
  assert(not interestRates_enabled or interestRatePlus < inf, "Parameter interestRatePlus needs to be specified");
  assert(not interestRates_enabled or interestRateMinus < inf, "Parameter interestRateMinus needs to be specified");
  connect(u_minus, negativeRate.u) annotation(Line(visible = true, origin = {-131.5, 40}, points = {{-13.5, 0}, {13.5, 0}}, color = {0, 0, 128}));
  connect(negativeRate.y, netRate.u2) annotation(Line(visible = true, origin = {-95, 40}, points = {{-7, 0}, {7, 0}}, color = {1, 37, 163}));
  connect(u_plus, netRate.u1) annotation(Line(visible = true, origin = {-108.25, 55}, points = {{-36.75, 5}, {8.25, 5}, {8.25, -5}, {20.25, -5}}, color = {0, 0, 128}));
  connect(u_plus, netRateInput.u) annotation(Line(visible = true, origin = {-108.25, 70}, points = {{-36.75, -10}, {8.25, -10}, {8.25, 10}, {20.25, 10}}, color = {0, 0, 128}));
  connect(netRateInput.y, netFlow.u) annotation(Line(visible = true, origin = {-60.667, 63.333}, points = {{-11.333, 16.667}, {5.667, 16.667}, {5.667, -33.333}}, color = {1, 37, 163}));
  connect(netRate.y, netFlow.u) annotation(Line(visible = true, origin = {-60.667, 40}, points = {{-11.333, 5}, {5.667, 5}, {5.667, -10}}, color = {1, 37, 163}));
  connect(netFlow.massPort, accountBalance.inflow) annotation(Line(visible = true, origin = {-25, 10}, points = {{-15, 10}, {0, 10}, {0, 10}, {15, 10}}, color = {128, 0, 128}));
  connect(accountBalance.y, y) annotation(Line(visible = true, origin = {57.337, 36.8}, points = {{-52.337, -6.4}, {-52.337, 3.2}, {104.673, 3.2}}, color = {1, 37, 163}));
  connect(accountBalance.y2, positiveBalance.u) annotation(Line(visible = true, origin = {-20.75, 2.5}, points = {{10.25, 12.5}, {5.75, 12.5}, {5.75, -12.5}, {-21.25, -12.5}}, color = {1, 37, 163}));
  connect(accountBalance.y1, invertedBalance.u) annotation(Line(visible = true, origin = {16.25, 15}, points = {{-5.75, 0}, {5.75, 0}}, color = {1, 37, 163}));
  connect(invertedBalance.y, negativeBalance.u) annotation(Line(visible = true, origin = {45, 15}, points = {{-7, 0}, {7, -0}}, color = {1, 37, 163}));
  connect(interestPaid.massPort, accountBalance.outflow) annotation(Line(visible = true, origin = {15, -10}, points = {{15, -20}, {0, -20}, {0, 30}, {-5, 30}}, color = {128, 0, 128}));
  connect(rateDecline.y, interestPaid.u) annotation(Line(visible = true, origin = {50.667, -13.333}, points = {{11.333, 3.333}, {-5.667, 3.333}, {-5.667, -6.667}}, color = {1, 37, 163}));
  connect(negativeBalance.y, rateDecline.u1) annotation(Line(visible = true, origin = {82, 5}, points = {{-14, 10}, {8, 10}, {8, -10}, {-4, -10}}, color = {1, 37, 163}));
  connect(interestMinus.y, rateDecline.u2) annotation(Line(visible = true, origin = {46, -47.5}, points = {{-160, -32.5}, {54, -32.5}, {54, 32.5}, {32, 32.5}}, color = {1, 37, 163}));
  connect(u_interestMinus, rateDecline.u2) annotation(Line(visible = true, origin = {33.25, -37.5}, points = {{-178.25, -22.5}, {66.75, -22.5}, {66.75, 22.5}, {44.75, 22.5}}, color = {0, 0, 128}));
  connect(interestPlus.y, rateIncrease.u2) annotation(Line(visible = true, origin = {-100.5, -40}, points = {{-13.5, -10}, {0.5, -10}, {0.5, 10}, {12.5, 10}}, color = {1, 37, 163}));
  connect(positiveBalance.y, rateIncrease.u1) annotation(Line(visible = true, origin = {-86.5, -15}, points = {{28.5, 5}, {-13.5, 5}, {-13.5, -5}, {-1.5, -5}}, color = {1, 37, 163}));
  connect(u_interestPlus, rateIncrease.u2) annotation(Line(visible = true, origin = {-108.25, -35}, points = {{-36.75, -5}, {8.25, -5}, {8.25, 5}, {20.25, 5}}, color = {0, 0, 128}));
  connect(rateIncrease.y, interestEarned.u) annotation(Line(visible = true, origin = {-51.766, -28.333}, points = {{-20.234, 3.333}, {10.117, 3.333}, {10.117, -6.667}}, color = {1, 37, 163}));
  connect(interestEarned.massPort, accountBalance.inflow) annotation(Line(visible = true, origin = {-19.162, -12.5}, points = {{-7.487, -32.5}, {-0.838, -32.5}, {-0.838, 32.5}, {9.162, 32.5}}, color = {128, 0, 128}));
  connect(accountBalance.outflow, outflow) annotation(Line(visible = true, origin = {47.5, 10}, points = {{-37.5, 10}, {-32.5, 10}, {-32.5, -10}, {102.5, -10}}, color = {128, 0, 128}));
  connect(inflow, accountBalance.inflow) annotation(Line(visible = true, origin = {-50, 10}, points = {{-100, -10}, {30, -10}, {30, 10}, {40, 10}}, color = {128, 0, 128}));
  connect(interestPaid.y2, y_intPaid) annotation(Line(visible = true, origin = {106.121, -35}, points = {{-55.621, 0}, {55.621, 0}}, color = {1, 37, 163}));
  connect(interestEarned.y1, y_intEarned) annotation(Line(visible = true, origin = {67.912, -50}, points = {{-94.061, 0}, {94.061, 0}}, color = {1, 37, 163}));
  annotation(Documentation(info = "<html>
<p class=\"aside\">This information is part of the Business Simulation&nbsp;Library (BSL). Please support this work and <a href=\"https://www.paypal.com/donate/?hosted_button_id=GXVZT8LD7CFXN\" style=\"font-weight:bold; color:orange; text-decoration:none;\">&#9658;&nbsp;donate</a>.</p>
<p>The output <strong>y</strong> reports the balance of an <em>account</em> that is increased or decreased by flows connected to the stock ports or at given rates (<code>u_plus</code> and <code>u_minus</code> respectively) if <code>hasRateInputs = true</code>. When there are interest payments (<code>withInterest = true</code>) positive and negative balances of the account will lead to <em>interest earned</em> and <em>interest paid</em>, respectively. The associated fractional rates can be given either as constants or as variable inputs.</p> 
<h4>Notes</h4>
<ul>
<li>Usually rates for deposits and withdrawals are given as separate, <em>positive</em> inputs. Using <code>hasNetRateInput = true</code> the rate of flow for the account can be given as a netRate using the connector <code>u_plus</code> only.</li><br>
<li>Fractional rates are assumed to be <em>continuously compounding</em> rates (→<a href=\"modelica://BusinessSimulation.Converters.ForceOfInterest\">ForceOfInterest</a>).</li><br>
<li>The <code>Account</code> is a more general structure than the widely known <em>bathtub</em> [<a href=\"modelica://BusinessSimulation.UsersGuide.References\">6</a>, pp.10f.] </li>
</ul>
<h4>See also</h4>
<p>
<a href=\"modelica://BusinessSimulation.MoleculesOfStructure.Transceivers.Reservoir\">Reservoir</a>
</p>
</html>"), Icon(coordinateSystem(extent = {{-100, -100}, {100, 100}}, preserveAspectRatio = true, initialScale = 0.1, grid = {10, 10}), graphics = {Line(visible = true, origin = {-37.335, -106.294}, points = {{47.335, 101.43}, {57.335, 87.889}, {47.335, 79.179}}, color = {192, 192, 192}, thickness = 2, smooth = Smooth.Bezier), Text(visible = true, origin = {0, 75}, textColor = {255, 0, 0}, extent = {{-100, -12}, {100, 12}}, textString = "Account", fontName = "Lato", textStyle = {TextStyle.Bold}), Line(visible = true, origin = {5.84, -0.234}, points = {{0, 0}, {28.109, 0}}, color = {0, 128, 0}, thickness = 5), Ellipse(visible = true, origin = {19.978, 0}, lineColor = {0, 128, 0}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid, lineThickness = 0.5, extent = {{-4.138, -4.138}, {4.138, 4.138}}), Rectangle(visible = true, lineColor = {255, 0, 0}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid, lineThickness = 3, extent = {{-10, -10}, {10, 10}}), Polygon(visible = true, origin = {34.453, -0.275}, lineColor = {0, 128, 0}, fillColor = {0, 128, 0}, fillPattern = FillPattern.Solid, points = {{-2.773, 3.823}, {-2.773, -3.863}, {5.547, 0.041}}), Line(visible = true, origin = {-46.16, -0.234}, points = {{0, 0}, {28.109, 0}}, color = {0, 128, 0}, thickness = 5), Ellipse(visible = true, origin = {-32.022, 0}, lineColor = {0, 128, 0}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid, lineThickness = 0.5, extent = {{-4.138, -4.138}, {4.138, 4.138}}), Polygon(visible = true, origin = {-17.547, -0.275}, lineColor = {0, 128, 0}, fillColor = {0, 128, 0}, fillPattern = FillPattern.Solid, points = {{-2.773, 3.823}, {-2.773, -3.863}, {5.547, 0.041}}), Line(visible = true, origin = {0, -48.109}, rotation = -270, points = {{0, 0}, {28.109, 0}}, color = {192, 192, 192}, thickness = 5), Ellipse(visible = true, origin = {0, -32.008}, rotation = -270, lineColor = {192, 192, 192}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid, lineThickness = 0.5, extent = {{-4.138, -4.138}, {4.138, 4.138}}), Polygon(visible = true, origin = {0.149, -18.15}, rotation = -270, lineColor = {192, 192, 192}, fillColor = {192, 192, 192}, fillPattern = FillPattern.Solid, points = {{-2.773, 3.823}, {-2.773, -3.863}, {5.547, 0.041}}), Polygon(visible = true, origin = {0.02, -48.613}, rotation = -450, lineColor = {192, 192, 192}, fillColor = {192, 192, 192}, fillPattern = FillPattern.Solid, points = {{-2.773, 3.823}, {-2.773, -3.863}, {5.547, 0.041}}), Polygon(visible = true, origin = {7.966, -28.246}, rotation = -153.312, lineColor = {192, 192, 192}, fillColor = {192, 192, 192}, fillPattern = FillPattern.Solid, points = {{-2.773, 2.672}, {-2.773, -2.623}, {3.945, -0.02}}), Text(visible = true, origin = {-42.691, -60.518}, textColor = {192, 192, 192}, extent = {{-35.466, -6}, {35.466, 6}}, textString = "interestRate", fontName = "Lato", textStyle = {TextStyle.Bold}), Text(visible = true, origin = {-32.399, 14}, textColor = {0, 128, 0}, extent = {{-9.174, -6}, {9.174, 6}}, textString = "+", fontName = "Lato"), Text(visible = true, origin = {20, 14}, textColor = {0, 128, 0}, extent = {{-9.174, -6}, {9.174, 6}}, textString = "–", fontName = "Lato", textStyle = {TextStyle.Bold}), Line(visible = true, origin = {-80.11, 43.016}, rotation = -8.77, points = {{71.259, -85.862}, {74.2, -75.29}, {81.612, -69.18}}, color = {192, 192, 192}, thickness = 2, smooth = Smooth.Bezier), Polygon(visible = true, origin = {-8.034, -36.411}, rotation = -153.312, lineColor = {192, 192, 192}, fillColor = {192, 192, 192}, fillPattern = FillPattern.Solid, points = {{2.773, -2.672}, {2.773, 2.623}, {-3.945, 0.02}})}), Diagram(coordinateSystem(extent = {{-150, -90}, {150, 90}}, preserveAspectRatio = true, initialScale = 0.1, grid = {5, 5})));
end Account;
